#include "colisiones.hpp"


namespace bp
{
  
/*
	SDL surface test if offset (u,v) is a transparent pixel
*/

bool Colisiones::CollideTransparentPixelTest(SDL_Surface* surface, int32_t u, int32_t v)
{
	/*assert that (u,v) offsets lie within surface*/
	assert( !((u < surface->w) || (v < surface->h)) );

	int32_t bpp = surface->format->BytesPerPixel;
	/*here p is the address to the pixel we want to retrieve*/
	uint8_t* p = (uint8_t*)surface->pixels + v * surface->pitch + u * bpp;

	uint32_t pixelcolor;

	switch(bpp)
	{
		case(1):
			pixelcolor = *p;
		break;

		case(2):
			pixelcolor = static_cast<uint32_t>(*p);
		break;

		case(3):
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				pixelcolor = static_cast<uint32_t>(p[0] << 16 | p[1] << 8 | p[2]);
			else
				pixelcolor = static_cast<uint32_t>(p[0] | p[1] << 8 | p[2] << 16);
		break;

		case(4):
			pixelcolor = static_cast<uint32_t>(*p);
		break;
	}

	/*test whether pixels color == color of transparent pixels for that surface*/
	return (pixelcolor == surface->format->colorkey);
}
 
/*
 bool Colisiones::CollideTransparentPixelTest(SDL_Surface* surface, int32_t u, int32_t v)
 {
	assert( !((u < surface->w) || (v < surface->h)) );
    
	int32_t bpp = surface->format->BytesPerPixel;
	uint8_t *p = (Uint8 *)surface->pixels + v * surface->pitch + u * bpp;
    
	uint32_t pixelcolor;
    
	switch(bpp)
	{
		case(1):
			pixelcolor = *p;
            break;
            
		case(2):
			pixelcolor = *(Uint16 *)p;
            break;
            
		case(3):
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				pixelcolor = p[0] << 16 | p[1] << 8 | p[2];
                else
                    pixelcolor = p[0] | p[1] << 8 | p[2] << 16;
                    break;
            
		case(4):
			pixelcolor = *(Uint32 *)p;
            break;
	}
    	return (pixelcolor == surface->format->colorkey);
}
 */
    
 /*
	SDL pixel perfect collision test
*/
bool Colisiones::CollidePixel(SDL_Surface* as, int32_t ax, int32_t ay, SDL_Surface* bs, int32_t bx, int32_t by)
{
	/*Box A;
	Box B;*/

	/*a - bottom right co-ordinates*/
	int32_t ax1 = ax + as->w - 1;
	int32_t ay1 = ay + as->h - 1;
	
	/*b - bottom right co-ordinates*/
	int32_t bx1 = bx + bs->w - 1;
	int32_t by1 = by + bs->h - 1;

	/*check if bounding boxes intersect*/
	if((bx1 < ax) || (ax1 < bx))
		return false;
	if((by1 < ay) || (ay1 < by))
		return false;


/*Now lets make the bouding box for which we check for a pixel collision*/

	/*To get the bounding box we do
	    Ax1,Ay1_____________
		|		|
		|		|
		|		|
		|    Bx1,By1_____________
		|	|	|	|
		|	|	|	|
		|_______|_______|	|
			|    Ax2,Ay2	|
			|		|
			|		|
			|____________Bx2,By2

	To find that overlap we find the biggest left hand cordinate
	AND the smallest right hand co-ordinate

	To find it for y we do the biggest top y value
	AND the smallest bottom y value

	Therefore the overlap here is Bx1,By1 --> Ax2,Ay2

	Remember	Ax2 = Ax1 + SA->w
			Bx2 = Bx1 + SB->w

			Ay2 = Ay1 + SA->h
			By2 = By1 + SB->h
	*/

	/*now we loop round every pixel in area of
	intersection
		if 2 pixels alpha values on 2 surfaces at the
		same place != 0 then we have a collision*/
	int32_t inter_x0 = SDL_COLLIDE_MAX(ax,bx);
	int32_t inter_x1 = SDL_COLLIDE_MIN(ax1,bx1);

	int32_t inter_y0 = SDL_COLLIDE_MAX(ay,by);
	int32_t inter_y1 = SDL_COLLIDE_MIN(ay1,by1);

	for(int32_t y = inter_y0 ; y <= inter_y1 ; y++)
	{
		for(int32_t x = inter_x0 ; x <= inter_x1 ; x++)
		{
			/*compute offsets for surface
			before pass to TransparentPixel test*/
			if((CollideTransparentPixelTest(as , x-ax , y-ay)) && (CollideTransparentPixelTest(bs , x-bx , y-by)))
				return true;
		}
	}
	
	return false;
}

/*
	SDL bounding box collision test
*/
bool Colisiones::CollideBoundingBox(SDL_Surface* sa, int32_t ax, int32_t ay, SDL_Surface* sb, int32_t bx, int32_t by)
{
	if(bx + sb->w < ax)	return false;	//just checking if their
	if(bx > ax + sa->w)	return false;	//bounding boxes even touch

	if(by + sb->h < ay)	return false;
	if(by > ay + sa->h)	return false;

	return true;				//bounding boxes intersect
}

/*
	SDL bounding box collision tests (works on SDL_Rect's)
*/
bool Colisiones::CollideBoundingBox(SDL_Rect a, SDL_Rect b)
{
	if(b.x + b.w < a.x)	return false;	//just checking if their
	if(b.x > a.x + a.w)	return false;	//bounding boxes even touch

	if(b.y + b.h < a.y)	return false;
	if(b.y > a.y + a.h)	return false;

	return true;				//bounding boxes intersect
}


/*
	tests whether 2 circles intersect

	circle1 : centre (x1,y1) with radius r1
	circle2 : centre (x2,y2) with radius r2
	
	(allow distance between circles of offset)
*/
bool Colisiones::CollideBoundingCircle(int32_t x1, int32_t y1, int32_t r1, int32_t x2, int32_t y2, int32_t r2, int32_t offset)
{
	int32_t xdiff = x2 - x1;	// x plane difference
	int32_t ydiff = y2 - y1;	// y plane difference
	
	/* distance between the circles centres squared */
	int32_t dcentre_sq = (ydiff*ydiff) + (xdiff*xdiff);
	
	/* calculate sum of radiuses squared */
	int32_t r_sum_sq = r1 + r2;	// square on seperate line, so
	r_sum_sq *= r_sum_sq;	// dont recompute r1 + r2

	return (dcentre_sq - r_sum_sq <= (offset*offset));
}

/*
	a circle intersection detection algorithm that will use
	the position of the centre of the surface as the centre of
	the circle and approximate the radius using the width and height
	of the surface (for example a rect of 4x6 would have r = 2.5).
*/
bool Colisiones::CollideBoundingCircle(SDL_Surface* a, int32_t x1, int32_t y1, SDL_Surface* b, int32_t x2, int32_t y2, int32_t offset)
{
	/* if radius is not specified
	we approximate them using SDL_Surface's
	width and height average and divide by 2*/
	int32_t r1 = (a->w + a->h) / 4;	// same as / 2) / 2;
	int32_t r2 = (b->w + b->h) / 4;

	x1 += a->w / 2;		// offset x and y
	y1 += a->h / 2;		// co-ordinates into
				// centre of image
	x2 += b->w / 2;
	y2 += b->h / 2;

	return CollideBoundingCircle(x1, y1, r1, x2, y2, r2, offset);
}
  
} // namespace bp
